package org.example.collection;

import cn.hutool.core.map.MapUtil;
import com.google.common.collect.Lists;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;

/**
 * List 集合元素去重
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2022/3/25 9:16
 */
public class List2Test {

    /**
     * list 集合元素去重，Java Bean 对象需要重写 equals 方法
     *
     * @param list
     * @param <T>
     * @return
     */
    public static <T> List<T> listDuplicateRemoval(List<T> list) {
        Set<T> set = new LinkedHashSet<>();
        set.addAll(list);
        list.clear();
        list.addAll(set);
        return list;
    }

    /**
     * list 集合元素去重，Java Bean 对象需要重写 equals 方法
     *
     * @param list
     * @param <T>
     * @return
     */
    public static <T> List<T> listDuplicateRemoval2(List<T> list) {
        Set<T> set = new LinkedHashSet<>(list);
        list.clear();
        list.addAll(set);
        return list;
    }

    /**
     * list 集合元素去重，Java Bean 对象需要重写 equals 方法。
     * Java 8 写法。
     *
     * @param list
     * @param <T>
     */
    public static <T> List<T> listDuplicateRemoval3(List<T> list) {
        return list.stream().distinct().collect(Collectors.toList());
    }

    /**
     * Stream<T> distinct() ：对元素进行去重
     */
    @SuppressWarnings("all")
    @Test
    public void distinct2() {
        Map<String, Object> dataMap1 = new HashMap<>();
        Map<String, Object> dataMap2 = new HashMap<>();
        Map<String, Object> dataMap3 = new HashMap<>();
        Map<String, Object> dataMap4 = new HashMap<>();
        Map<String, Object> dataMap5 = new HashMap<>();

        dataMap1.put("init_agency_id", "20792");
        dataMap2.put("init_agency_id", "20793");
        dataMap3.put("init_agency_id", "20794");
        dataMap4.put("init_agency_id", "20792");
        dataMap5.put("init_agency_id", "20795");
        dataMap1.put("agency_code", "002001");
        dataMap2.put("agency_code", "002002");
        dataMap3.put("agency_code", "002003");
        dataMap4.put("agency_code", "002004");
        dataMap5.put("agency_code", "002005");

        List<Map<String, Object>> agencyList = new ArrayList<>();
        agencyList.add(dataMap1);
        agencyList.add(dataMap2);
        agencyList.add(dataMap3);
        agencyList.add(dataMap4);
        agencyList.add(dataMap5);

        List<String> newAgencyList = agencyList.stream().map(pm -> MapUtil.getStr(pm, "init_agency_id")).distinct().collect(Collectors.toList());
        //[20792, 20793, 20794, 20795]
        System.out.println(newAgencyList);

        List<String> newAgencyList2 = agencyList.stream().map(pm -> MapUtil.getStr(pm, "init_agency_id2")).distinct().collect(Collectors.toList());
        // [null]
        System.out.println(newAgencyList2);
    }

    /**
     * 对 List<Map<String,Object>> 数据去重的同时进行排序
     */
    @Test
    public void testCollectingAndThen() {
        List<Map<String, String>> list = new ArrayList<>();
        Map<String, String> map1 = new HashMap<>();
        Map<String, String> map2 = new HashMap<>();
        Map<String, String> map3 = new HashMap<>();
        Map<String, String> map4 = new HashMap<>();

        map1.put("agency_id", "20793");
        map1.put("agency_name", "单位3");
        map1.put("agency_codee", "001003");

        map2.put("agency_id", "20792");
        map2.put("agency_name", "单位21");
        map2.put("agency_codee", "001002");

        map3.put("agency_id", "20791");
        map3.put("agency_name", "单位1");
        map3.put("agency_codee", "001001");

        map4.put("agency_id", "20792");
        map4.put("agency_name", "单位2");
        map4.put("agency_codee", "001002");

        list.add(map1);
        list.add(map2);
        list.add(map3);
        list.add(map4);

        // {agency_codee=001001, agency_id=20791, agency_name=单位1},
        // {agency_codee=001002, agency_id=20792, agency_name=单位2},
        // {agency_codee=001003, agency_id=20793, agency_name=单位3},
        // {agency_codee=001002, agency_id=20792, agency_name=单位21}
        System.out.println("去重前：" + list);

        // 根据单位ID进行去重，同时根据它进行排序.
        list = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                new TreeSet<>(Comparator.comparing((o) -> o.get("agency_id")))), ArrayList::new));

        // {agency_codee=001001, agency_id=20791, agency_name=单位1},
        // {agency_codee=001002, agency_id=20792, agency_name=单位2},
        // {agency_codee=001003, agency_id=20793, agency_name=单位3}
        System.out.println("去重后：" + list);
    }

    @Test
    public void testListDuplicateRemoval1() {
        ArrayList<String> list = Lists.newArrayList("78", "Au", "大唐", "78", "大唐", "AuT");
        //[78, Au, 大唐, AuT]
        System.out.println(listDuplicateRemoval(list));

        list = Lists.newArrayList("78", "Au", "大唐", "78", "大唐", "AuT");
        //[78, Au, 大唐, AuT]
        System.out.println(listDuplicateRemoval2(list));

        list = Lists.newArrayList("78", "Au", "大唐", "78", "大唐", "AuT");
        //[78, Au, 大唐, AuT]
        System.out.println(listDuplicateRemoval3(list));
    }

    @Test
    public void testListDuplicateRemoval2() {
        Date birthday = new Date();
        Person person1 = new Person("1", "张三", 45, birthday);
        Person person2 = new Person("2", "张三", 45, birthday);

        ArrayList<Person> list = Lists.newArrayList(person1, person2, person1, person2, person2);
        // [Person{pid='1', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022},
        // Person{pid='2', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022},
        // Person{pid='1', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022},
        // Person{pid='2', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022},
        // Person{pid='2', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022}]
        System.out.println(list);
        System.out.println();
        // [Person{pid='1', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022},
        // Person{pid='2', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022}]
        System.out.println(listDuplicateRemoval(list));
        System.out.println();

        list = Lists.newArrayList(person1, person2, person1, person2, person2);
        System.out.println(listDuplicateRemoval2(list));
        System.out.println();

        list = Lists.newArrayList(person1, person2, person1, person2, person2);
        // [Person{pid='1', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022},
        // Person{pid='2', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022}]
        System.out.println(listDuplicateRemoval3(list));
        System.out.println();

        // [Person{pid='1', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022},
        // Person{pid='2', pname='张三', age=45, birthday=Mon Aug 29 19:49:10 CST 2022}]
        System.out.println(list);
    }

    /**
     * 笛卡尔积 演示 1
     *
     * @param allList ：格式 List<List<T>>，对每一个 List 子集合求笛卡儿积。从头开始，每两个求一次。
     *                如 原数据：[[1, 2], [1, 2], [1, 2, 3], [1]]
     *                笛卡尔积：
     *                [1, 1, 1, 1]
     *                [1, 1, 2, 1]
     *                [1, 1, 3, 1]
     *                [1, 2, 1, 1]
     *                [1, 2, 2, 1]
     *                [1, 2, 3, 1]
     *                [2, 1, 1, 1]
     *                [2, 1, 2, 1]
     *                [2, 1, 3, 1]
     *                [2, 2, 1, 1]
     *                [2, 2, 2, 1]
     *                [2, 2, 3, 1]
     * @return
     */
    public static List<List> descartes1(List<List> allList) {
        if (allList == null || allList.isEmpty()) {
            return new ArrayList<>();
        }
        if (allList.size() == 1) {
            return allList.get(0);
        }
        // 组合的最终结果
        List<List> result = new ArrayList();
        // 第一个子集合，第1和第2个子集合合并后，list0 会变成 List<List> 格式.
        List preList = allList.get(0);
        for (int i = 1; i < allList.size(); i++) {
            List list_i = allList.get(i);
            // 前一个集合与后一个合并后的结果，一直到合并完成。
            List tempList = new ArrayList();
            // 每次先计算两个集合的笛卡尔积，然后用其结果再与下一个计算
            for (int j = 0; j < preList.size(); j++) {
                for (int k = 0; k < list_i.size(); k++) {
                    // 用于组装前一个集合中的每个元素与下一个求笛卡尔积
                    List cut = new ArrayList();
                    // 第1个集合和第2个集合合并时，preList 是 List，然后与第3个集合合并时，preList 是 List<List>
                    if (preList.get(j) instanceof List) {
                        cut.addAll((List) preList.get(j));
                    } else {
                        cut.add(preList.get(j));
                    }
                    cut.add(list_i.get(k));
                    tempList.add(cut);
                }
            }
            // 第1个与第2个合并完成后，preList 就变成了 List<List>，然后继续与后面的子集合求笛卡尔积.
            preList = tempList;
            if (i >= allList.size() - 1) {
                result = tempList;
            }
        }
        return result;
    }

    /**
     * 笛卡尔积 演示 2
     *
     * @param allList ：对每一个 List 子集合求笛卡儿积。从头开始，每两个求一次。
     * @return
     */
    public static List<Map<String, Object>> descartes2(List<List<Map<String, Object>>> allList) {
        // 组合的最终结果
        List<Map<String, Object>> result = new ArrayList();
        if (allList == null || allList.isEmpty()) {
            return result;
        }
        if (allList.size() == 1) {
            return allList.get(0);
        }
        // 第一个子集合
        List<Map<String, Object>> preList = allList.get(0);
        for (int i = 1; i < allList.size(); i++) {
            List<Map<String, Object>> list_i = allList.get(i);
            List<Map<String, Object>> tempList = new ArrayList();
            // 每次先计算两个集合的笛卡尔积，然后用其结果再与下一个计算
            for (int j = 0; j < preList.size(); j++) {
                for (int k = 0; k < list_i.size(); k++) {
                    Map<String, Object> cut = new HashMap<>(16);
                    cut.putAll(preList.get(j));
                    cut.putAll(list_i.get(k));
                    tempList.add(cut);
                }
            }
            preList = tempList;
            if (i == allList.size() - 1) {
                result = tempList;
            }
        }
        return result;
    }

    /**
     * 笛卡尔积 测试 1
     */
    @Test
    public void testDescartes1() {
        ArrayList list1 = new ArrayList();
        list1.add(1);
        list1.add(2);

        ArrayList list2 = new ArrayList();
        list2.add("1");
        list2.add("2");

        ArrayList list3 = new ArrayList();
        list3.add("1");
        list3.add("2");
        list3.add("3");

        ArrayList list4 = new ArrayList();
        list4.add("1");

        ArrayList allList = new ArrayList();
        allList.add(list1);
        allList.add(list2);
        allList.add(list3);
        allList.add(list4);

        // 原数据：[[1, 2], [1, 2], [1, 2, 3], [1]]
        System.out.println("原数据：" + allList);
        List list = descartes1(allList);
        // 笛卡儿积大小=12
        System.out.println("笛卡儿积大小=" + list.size());
        /**
         * [1, 1, 1, 1]
         * [1, 1, 2, 1]
         * [1, 1, 3, 1]
         * [1, 2, 1, 1]
         * [1, 2, 2, 1]
         * [1, 2, 3, 1]
         * [2, 1, 1, 1]
         * [2, 1, 2, 1]
         * [2, 1, 3, 1]
         * [2, 2, 1, 1]
         * [2, 2, 2, 1]
         * [2, 2, 3, 1]
         */
        for (Object object : list) {
            System.out.println(object);
        }
    }


    /**
     * 笛卡尔积 测试 2
     */
    @Test
    public void testDescartes2() {
        List<List<Map<String, Object>>> checkBox = new ArrayList<>();
        List<Map<String, Object>> list1 = new ArrayList<>();
        Map<String, Object> map11 = new HashMap<>(16);
        map11.put("per_sta_id", "910");
        map11.put("per_sta_code", "1");
        map11.put("per_sta_name", "离休");

        Map<String, Object> map12 = new HashMap<>(16);
        map12.put("per_sta_id", "911");
        map12.put("per_sta_code", "2");
        map12.put("per_sta_name", "退休");

        list1.add(map11);
        list1.add(map12);

        List<Map<String, Object>> list2 = new ArrayList<>();
        Map<String, Object> map21 = new HashMap<>();
        map21.put("ui_id", "personnelType_202005");
        map21.put("ui_code", "202005");
        map21.put("ui_name", "行政人员");

        Map<String, Object> map22 = new HashMap<>();
        map22.put("ui_id", "personnelType_202006");
        map22.put("ui_code", "202006");
        map22.put("ui_name", "事业人员");

        list2.add(map21);
        list2.add(map22);

        List<Map<String, Object>> list3 = new ArrayList<>();
        Map<String, Object> map31 = new HashMap<>();
        map31.put("pay_sala_gr_id", "852");
        map31.put("pay_sala_gr_code", "04");
        map31.put("pay_sala_gr_name", "4档");

        Map<String, Object> map32 = new HashMap<>();
        map32.put("pay_sala_gr_id", "853");
        map32.put("pay_sala_gr_code", "05");
        map32.put("pay_sala_gr_name", "5档");

        Map<String, Object> map33 = new HashMap<>();
        map33.put("pay_sala_gr_id", "854");
        map33.put("pay_sala_gr_code", "06");
        map33.put("pay_sala_gr_name", "6档");

        list3.add(map31);
        list3.add(map32);
        list3.add(map33);

        checkBox.add(list1);
        checkBox.add(list2);
        checkBox.add(list3);

        /**
         * 原始数据：
         * [{per_sta_name=离休, per_sta_id=910, per_sta_code=1}, {per_sta_name=退休, per_sta_id=911, per_sta_code=2}]
         * [{ui_name=行政人员, ui_id=personnelType_202005, ui_code=202005}, {ui_name=事业人员, ui_id=personnelType_202006, ui_code=202006}]
         * [{pay_sala_gr_name=4档, pay_sala_gr_code=04, pay_sala_gr_id=852}, {pay_sala_gr_name=5档, pay_sala_gr_code=05, pay_sala_gr_id=853}, {pay_sala_gr_name=6档, pay_sala_gr_code=06, pay_sala_gr_id=854}]
         */
        System.out.println("原始数据：");
        checkBox.stream().forEach(item -> System.out.println(item));

        List<Map<String, Object>> list = descartes2(checkBox);
        // 笛卡儿积大小=12
        System.out.println("笛卡儿积大小=" + list.size());
        /**
         * {ui_name=行政人员, pay_sala_gr_name=4档, per_sta_name=离休, pay_sala_gr_code=04, ui_id=personnelType_202005, ui_code=202005, per_sta_id=910, per_sta_code=1, pay_sala_gr_id=852}
         * {ui_name=行政人员, pay_sala_gr_name=5档, per_sta_name=离休, pay_sala_gr_code=05, ui_id=personnelType_202005, ui_code=202005, per_sta_id=910, per_sta_code=1, pay_sala_gr_id=853}
         * {ui_name=行政人员, pay_sala_gr_name=6档, per_sta_name=离休, pay_sala_gr_code=06, ui_id=personnelType_202005, ui_code=202005, per_sta_id=910, per_sta_code=1, pay_sala_gr_id=854}
         * {ui_name=事业人员, pay_sala_gr_name=4档, per_sta_name=离休, pay_sala_gr_code=04, ui_id=personnelType_202006, ui_code=202006, per_sta_id=910, per_sta_code=1, pay_sala_gr_id=852}
         * {ui_name=事业人员, pay_sala_gr_name=5档, per_sta_name=离休, pay_sala_gr_code=05, ui_id=personnelType_202006, ui_code=202006, per_sta_id=910, per_sta_code=1, pay_sala_gr_id=853}
         * {ui_name=事业人员, pay_sala_gr_name=6档, per_sta_name=离休, pay_sala_gr_code=06, ui_id=personnelType_202006, ui_code=202006, per_sta_id=910, per_sta_code=1, pay_sala_gr_id=854}
         * {ui_name=行政人员, pay_sala_gr_name=4档, per_sta_name=退休, pay_sala_gr_code=04, ui_id=personnelType_202005, ui_code=202005, per_sta_id=911, per_sta_code=2, pay_sala_gr_id=852}
         * {ui_name=行政人员, pay_sala_gr_name=5档, per_sta_name=退休, pay_sala_gr_code=05, ui_id=personnelType_202005, ui_code=202005, per_sta_id=911, per_sta_code=2, pay_sala_gr_id=853}
         * {ui_name=行政人员, pay_sala_gr_name=6档, per_sta_name=退休, pay_sala_gr_code=06, ui_id=personnelType_202005, ui_code=202005, per_sta_id=911, per_sta_code=2, pay_sala_gr_id=854}
         * {ui_name=事业人员, pay_sala_gr_name=4档, per_sta_name=退休, pay_sala_gr_code=04, ui_id=personnelType_202006, ui_code=202006, per_sta_id=911, per_sta_code=2, pay_sala_gr_id=852}
         * {ui_name=事业人员, pay_sala_gr_name=5档, per_sta_name=退休, pay_sala_gr_code=05, ui_id=personnelType_202006, ui_code=202006, per_sta_id=911, per_sta_code=2, pay_sala_gr_id=853}
         * {ui_name=事业人员, pay_sala_gr_name=6档, per_sta_name=退休, pay_sala_gr_code=06, ui_id=personnelType_202006, ui_code=202006, per_sta_id=911, per_sta_code=2, pay_sala_gr_id=854}
         */
        list.stream().forEach(item -> System.out.println(item));
    }

}
